import urllib.request
import urllib.parse
import urllib.error
import json
import os
import time
import random
from datetime import datetime, timedelta


class WeatherService:
    """
    天气服务类，提供天气数据获取功能
    支持多种天气API和模拟天气数据生成
    """
    
    def __init__(self, config=None):
        # 天气服务配置
        self.config = config or {
            'api_provider': 'mock',  # 默认使用模拟数据
            'api_key': '',  # API密钥，实际使用时需要填入
            'location': {'city': '北京', 'latitude': 39.9042, 'longitude': 116.4074},
            'cache_dir': os.path.join(os.path.dirname(__file__), '..', 'data', 'weather_cache'),
            'cache_expire_hours': 1,  # 缓存过期时间（小时）
        }
        
        # 初始化缓存目录
        os.makedirs(self.config['cache_dir'], exist_ok=True)
        
        # API基础URL配置
        self.api_urls = {
            'openweathermap': 'https://api.openweathermap.org/data/2.5/weather',
            'heweather': 'https://devapi.qweather.com/v7/weather/now',
            'weatherapi': 'https://api.weatherapi.com/v1/current.json',
        }
    
    def get_weather_data(self, use_cache=True):
        """
        获取天气数据
        优先尝试缓存，然后是真实API调用，最后是模拟数据
        """
        # 尝试从缓存获取
        cache_key = f"{self.config['location']['city']}_{int(time.time() / 3600)}"
        cache_file = os.path.join(self.config['cache_dir'], f"{cache_key}.json")
        
        if use_cache and self._is_cache_valid(cache_file):
            try:
                with open(cache_file, 'r', encoding='utf-8') as f:
                    cached_data = json.load(f)
                    print(f"使用缓存的天气数据: {self.config['location']['city']}")
                    return cached_data
            except Exception as e:
                print(f"读取缓存失败: {e}")
        
        # 尝试真实API调用
        weather_data = None
        if self.config['api_provider'] != 'mock' and self.config['api_key']:
            try:
                weather_data = self._fetch_from_api()
            except Exception as e:
                print(f"天气API调用失败: {e}")
                print("切换到模拟天气数据")
        
        # 如果API调用失败，使用模拟数据
        if not weather_data:
            weather_data = self._generate_mock_weather()
        
        # 保存到缓存
        try:
            with open(cache_file, 'w', encoding='utf-8') as f:
                json.dump(weather_data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存缓存失败: {e}")
        
        return weather_data
    
    def _is_cache_valid(self, cache_file):
        """检查缓存是否有效"""
        if not os.path.exists(cache_file):
            return False
        
        # 检查文件年龄
        file_age = time.time() - os.path.getmtime(cache_file)
        expire_seconds = self.config['cache_expire_hours'] * 3600
        
        return file_age < expire_seconds
    
    def _fetch_from_api(self):
        """从真实API获取天气数据"""
        provider = self.config['api_provider']
        
        if provider == 'openweathermap':
            return self._fetch_openweathermap()
        elif provider == 'heweather':
            return self._fetch_heweather()
        elif provider == 'weatherapi':
            return self._fetch_weatherapi()
        else:
            raise ValueError(f"不支持的天气API提供商: {provider}")
    
    def _fetch_openweathermap(self):
        """获取OpenWeatherMap天气数据"""
        params = urllib.parse.urlencode({
            'lat': self.config['location']['latitude'],
            'lon': self.config['location']['longitude'],
            'appid': self.config['api_key'],
            'units': 'metric',  # 使用摄氏度
            'lang': 'zh_cn',    # 中文输出
        })
        
        url = f"{self.api_urls['openweathermap']}?{params}"
        
        try:
            with urllib.request.urlopen(url, timeout=10) as response:
                data = json.loads(response.read().decode('utf-8'))
                
                # 转换为统一格式
                return {
                    'timestamp': datetime.now().isoformat(),
                    'temperature': data['main']['temp'],
                    'feels_like': data['main']['feels_like'],
                    'humidity': data['main']['humidity'],
                    'wind_speed': data['wind']['speed'],
                    'weather': data['weather'][0]['description'],
                    'weather_icon': data['weather'][0]['icon'],
                    'is_raining': data['weather'][0]['main'] in ['Rain', 'Drizzle', 'Thunderstorm'],
                    'rain': data.get('rain', {}).get('1h', 0) if 'rain' in data else 0,
                    'raw_data': data,
                }
        except urllib.error.HTTPError as e:
            raise Exception(f"HTTP错误: {e.code} {e.reason}")
        except urllib.error.URLError as e:
            raise Exception(f"网络错误: {str(e)}")
        except Exception as e:
            raise Exception(f"获取OpenWeatherMap数据失败: {str(e)}")
    
    def _fetch_heweather(self):
        """获取和风天气数据"""
        params = urllib.parse.urlencode({
            'location': f"{self.config['location']['longitude']},{self.config['location']['latitude']}",
            'key': self.config['api_key'],
        })
        
        url = f"{self.api_urls['heweather']}?{params}"
        
        try:
            with urllib.request.urlopen(url, timeout=10) as response:
                data = json.loads(response.read().decode('utf-8'))
                
                if data['code'] != '200':
                    raise Exception(f"和风天气API错误: {data.get('message', '未知错误')}")
                
                now = data['now']
                # 转换为统一格式
                return {
                    'timestamp': datetime.now().isoformat(),
                    'temperature': float(now['temp']),
                    'feels_like': float(now['feelsLike']),
                    'humidity': int(now['humidity']),
                    'wind_speed': float(now['windSpeed']),
                    'weather': now['text'],
                    'weather_icon': now['icon'],
                    'is_raining': now['text'] in ['小雨', '中雨', '大雨', '暴雨'],
                    'rain': 0,  # 和风天气当前API不直接提供降雨量
                    'raw_data': data,
                }
        except urllib.error.HTTPError as e:
            raise Exception(f"HTTP错误: {e.code} {e.reason}")
        except urllib.error.URLError as e:
            raise Exception(f"网络错误: {str(e)}")
        except Exception as e:
            raise Exception(f"获取和风天气数据失败: {str(e)}")
    
    def _fetch_weatherapi(self):
        """获取WeatherAPI数据"""
        params = urllib.parse.urlencode({
            'key': self.config['api_key'],
            'q': f"{self.config['location']['latitude']},{self.config['location']['longitude']}",
            'aqi': 'no',
        })
        
        url = f"{self.api_urls['weatherapi']}?{params}"
        
        try:
            with urllib.request.urlopen(url, timeout=10) as response:
                data = json.loads(response.read().decode('utf-8'))
                
                current = data['current']
                # 转换为统一格式
                return {
                    'timestamp': datetime.now().isoformat(),
                    'temperature': current['temp_c'],
                    'feels_like': current['feelslike_c'],
                    'humidity': current['humidity'],
                    'wind_speed': current['wind_kph'] / 3.6,  # 转换为m/s
                    'weather': current['condition']['text'],
                    'weather_icon': current['condition']['icon'],
                    'is_raining': current['is_raining'] if 'is_raining' in current else False,
                    'rain': current.get('precip_mm', 0),
                    'raw_data': data,
                }
        except urllib.error.HTTPError as e:
            raise Exception(f"HTTP错误: {e.code} {e.reason}")
        except urllib.error.URLError as e:
            raise Exception(f"网络错误: {str(e)}")
        except Exception as e:
            raise Exception(f"获取WeatherAPI数据失败: {str(e)}")
    
    def _generate_mock_weather(self):
        """
        生成模拟天气数据
        根据季节和时间生成合理的模拟数据
        """
        now = datetime.now()
        month = now.month
        hour = now.hour
        
        # 根据季节设置温度范围
        if 3 <= month <= 5:  # 春季
            temp_base = random.uniform(10, 25)
        elif 6 <= month <= 8:  # 夏季
            temp_base = random.uniform(25, 35)
        elif 9 <= month <= 11:  # 秋季
            temp_base = random.uniform(15, 25)
        else:  # 冬季
            temp_base = random.uniform(-5, 15)
        
        # 根据时间调整温度
        if 6 <= hour <= 10:  # 早晨
            temp_variation = -2
        elif 10 <= hour <= 16:  # 中午下午
            temp_variation = 2
        elif 16 <= hour <= 20:  # 傍晚
            temp_variation = 0
        else:  # 晚上夜间
            temp_variation = -4
        
        temperature = round(temp_base + temp_variation + random.uniform(-2, 2), 1)
        
        # 天气类型和概率
        weather_types = [
            ('晴天', 0.4),
            ('多云', 0.3),
            ('阴天', 0.15),
            ('小雨', 0.1),
            ('中雨', 0.03),
            ('大风', 0.02),
        ]
        
        # 根据季节调整天气概率
        if month in [6, 7, 8]:  # 夏季增加雨的概率
            weather_types = [
                ('晴天', 0.3),
                ('多云', 0.3),
                ('阴天', 0.1),
                ('小雨', 0.15),
                ('中雨', 0.1),
                ('雷阵雨', 0.05),
            ]
        elif month in [12, 1, 2]:  # 冬季可能下雪
            weather_types = [
                ('晴天', 0.45),
                ('多云', 0.3),
                ('阴天', 0.1),
                ('小雪', 0.05),
                ('中雪', 0.03),
                ('大风', 0.07),
            ]
        
        # 随机选择天气
        weather_names = [wt[0] for wt in weather_types]
        weather_probs = [wt[1] for wt in weather_types]
        weather = random.choices(weather_names, weights=weather_probs, k=1)[0]
        
        # 根据天气设置其他参数
        if '雨' in weather:
            is_raining = True
            rain = random.uniform(0.5, 10.0)
            humidity = random.uniform(70, 95)
            feels_like = temperature - 2  # 下雨时体感温度降低
        elif '雪' in weather:
            is_raining = False  # 下雪单独处理
            rain = 0
            humidity = random.uniform(60, 85)
            feels_like = temperature - 3  # 下雪时体感温度更低
        elif weather == '晴天':
            is_raining = False
            rain = 0
            humidity = random.uniform(30, 60)
            feels_like = temperature + 1  # 晴天时体感温度略高
        elif weather == '多云':
            is_raining = False
            rain = 0
            humidity = random.uniform(40, 70)
            feels_like = temperature
        elif weather == '阴天':
            is_raining = False
            rain = 0
            humidity = random.uniform(60, 80)
            feels_like = temperature - 1
        else:  # 大风等
            is_raining = False
            rain = 0
            humidity = random.uniform(40, 70)
            feels_like = temperature
        
        # 风速
        if weather == '大风':
            wind_speed = random.uniform(5, 10)
        elif '雨' in weather or '雪' in weather:
            wind_speed = random.uniform(3, 7)
        else:
            wind_speed = random.uniform(1, 5)
        
        # 生成模拟数据
        mock_data = {
            'timestamp': now.isoformat(),
            'temperature': round(temperature, 1),
            'feels_like': round(feels_like, 1),
            'humidity': round(humidity),
            'wind_speed': round(wind_speed, 1),
            'weather': weather,
            'weather_icon': self._get_weather_icon(weather),
            'is_raining': is_raining,
            'rain': round(rain, 1) if is_raining else 0,
            'is_mock_data': True,
        }
        
        return mock_data
    
    def _get_weather_icon(self, weather_text):
        """根据天气文本获取对应的图标代码"""
        icon_map = {
            '晴天': '100',
            '多云': '101',
            '阴天': '104',
            '小雨': '305',
            '中雨': '306',
            '大雨': '307',
            '雷阵雨': '302',
            '小雪': '400',
            '中雪': '401',
            '大雪': '402',
            '大风': '503',
        }
        
        for key, icon in icon_map.items():
            if key in weather_text:
                return icon
        
        return '101'  # 默认返回多云图标
    
    def is_good_for_running(self, weather_data=None):
        """
        根据天气数据判断是否适合跑步
        返回一个包含适合度和原因的字典
        """
        if not weather_data:
            weather_data = self.get_weather_data()
        
        temp = weather_data['temperature']
        feels_like = weather_data['feels_like']
        humidity = weather_data['humidity']
        wind_speed = weather_data['wind_speed']
        is_raining = weather_data['is_raining']
        weather = weather_data['weather']
        
        suitability = 100  # 初始适合度100%
        reasons = []
        recommendations = []
        
        # 根据天气状况调整适合度
        if is_raining or '雨' in weather or '雪' in weather:
            suitability -= 50
            reasons.append('当前正在下雨/雪，不适合户外跑步')
            recommendations.append('建议改为室内运动或在健身房跑步')
        
        # 根据温度判断
        if temp < 0:
            suitability -= 30
            reasons.append(f'温度过低({temp}°C)，容易导致肌肉拉伤')
            recommendations.append('请做好充分热身，穿着保暖运动服装')
        elif temp > 35:
            suitability -= 40
            reasons.append(f'温度过高({temp}°C)，容易中暑')
            recommendations.append('建议在早晨或傍晚温度较低时跑步')
        elif 10 <= temp <= 25:
            recommendations.append('温度适宜，是跑步的理想天气')
        
        # 根据体感温度调整
        if abs(feels_like - temp) > 5:
            reasons.append(f'体感温度({feels_like}°C)与实际温度差异较大')
            recommendations.append(f'请根据体感温度({feels_like}°C)穿着合适的衣物')
        
        # 根据湿度判断
        if humidity > 85:
            suitability -= 20
            reasons.append(f'湿度较高({humidity}%)，会影响汗液蒸发')
            recommendations.append('注意及时补充水分，调整跑步强度')
        elif humidity < 20:
            suitability -= 10
            reasons.append(f'湿度较低({humidity}%)，容易引起呼吸道不适')
            recommendations.append('跑步前多喝水，注意呼吸道保湿')
        
        # 根据风速判断
        if wind_speed > 7:
            suitability -= 15
            reasons.append(f'风速较大({wind_speed}m/s)，会增加跑步阻力')
            recommendations.append('建议选择背风路线，调整跑步节奏')
        
        # 确保适合度在0-100之间
        suitability = max(0, min(100, suitability))
        
        # 定义适合等级
        if suitability >= 80:
            level = '非常适合'
        elif suitability >= 60:
            level = '适合'
        elif suitability >= 40:
            level = '较适合'
        elif suitability >= 20:
            level = '不太适合'
        else:
            level = '不适合'
        
        return {
            'suitability_score': suitability,
            'suitability_level': level,
            'reasons': reasons,
            'recommendations': recommendations,
            'weather_summary': f"当前{weather}，温度{temp}°C，湿度{humidity}%，风速{wind_speed}m/s"
        }
    
    def get_daily_forecast(self, days=3):
        """获取每日天气预报（模拟实现）"""
        forecasts = []
        base_date = datetime.now()
        
        for i in range(days):
            forecast_date = base_date + timedelta(days=i)
            
            # 生成模拟预报数据
            # 注意：实际应用中应从API获取真实预报数据
            if i == 0:  # 今天使用实际获取的数据
                current_weather = self.get_weather_data()
                forecast = {
                    'date': forecast_date.strftime('%Y-%m-%d'),
                    'weekday': self._get_weekday_name(forecast_date.weekday()),
                    'temp_max': current_weather['temperature'] + 2,
                    'temp_min': current_weather['temperature'] - 3,
                    'weather': current_weather['weather'],
                    'is_raining': current_weather['is_raining'],
                }
            else:  # 未来几天生成模拟数据
                # 简单模拟：在当前温度基础上随机波动
                current_temp = random.uniform(10, 25)  # 这里可以改进为更智能的预测
                
                weather_types = ['晴天', '多云', '阴天', '小雨']
                weather = random.choice(weather_types)
                
                forecast = {
                    'date': forecast_date.strftime('%Y-%m-%d'),
                    'weekday': self._get_weekday_name(forecast_date.weekday()),
                    'temp_max': round(current_temp + random.uniform(2, 5), 1),
                    'temp_min': round(current_temp - random.uniform(3, 6), 1),
                    'weather': weather,
                    'is_raining': '雨' in weather,
                }
            
            forecasts.append(forecast)
        
        return forecasts
    
    def _get_weekday_name(self, weekday_num):
        """获取星期几的中文名称"""
        weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        return weekdays[weekday_num]
    
    def save_weather_record(self, weather_data, storage_manager=None):
        """保存天气记录到存储管理器"""
        if storage_manager:
            return storage_manager.save_weather_record(weather_data)
        return None


class WeatherConfigLoader:
    """
    天气配置加载器，从配置文件加载天气API设置
    """
    
    @staticmethod
    def load_config(config_path=None):
        """从配置文件加载天气API配置"""
        if config_path is None:
            config_path = os.path.join(os.path.dirname(__file__), '..', 'config', 'weather_config.json')
        
        if not os.path.exists(config_path):
            # 如果配置文件不存在，返回默认配置
            return WeatherConfigLoader._get_default_config()
        
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
                return config
        except Exception as e:
            print(f"加载天气配置文件失败: {e}")
            return WeatherConfigLoader._get_default_config()
    
    @staticmethod
    def _get_default_config():
        """获取默认配置"""
        return {
            'api_provider': 'mock',
            'api_key': '',
            'location': {'city': '北京', 'latitude': 39.9042, 'longitude': 116.4074},
            'cache_expire_hours': 1,
        }
    
    @staticmethod
    def save_config(config, config_path=None):
        """保存天气配置到文件"""
        if config_path is None:
            config_path = os.path.join(os.path.dirname(__file__), '..', 'config', 'weather_config.json')
        
        # 确保配置目录存在
        os.makedirs(os.path.dirname(config_path), exist_ok=True)
        
        try:
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
            return True
        except Exception as e:
            print(f"保存天气配置失败: {e}")
            return False


if __name__ == "__main__":
    # 示例使用
    print("天气服务测试")
    
    # 加载配置
    config = WeatherConfigLoader.load_config()
    
    # 创建天气服务实例
    weather_service = WeatherService(config)
    
    # 获取当前天气
    weather_data = weather_service.get_weather_data()
    print("\n当前天气数据:")
    print(f"温度: {weather_data['temperature']}°C")
    print(f"体感温度: {weather_data['feels_like']}°C")
    print(f"湿度: {weather_data['humidity']}%")
    print(f"风速: {weather_data['wind_speed']}m/s")
    print(f"天气: {weather_data['weather']}")
    print(f"是否下雨: {'是' if weather_data['is_raining'] else '否'}")
    
    # 判断是否适合跑步
    running_suitability = weather_service.is_good_for_running(weather_data)
    print("\n跑步适宜度分析:")
    print(f"适合度: {running_suitability['suitability_score']}% ({running_suitability['suitability_level']})")
    print("\n原因:")
    for reason in running_suitability['reasons']:
        print(f"- {reason}")
    print("\n建议:")
    for rec in running_suitability['recommendations']:
        print(f"- {rec}")
    
    # 获取天气预报
    print("\n未来3天天气预报:")
    forecasts = weather_service.get_daily_forecast(days=3)
    for forecast in forecasts:
        print(f"{forecast['date']} {forecast['weekday']}: {forecast['weather']}, 温度 {forecast['temp_min']}°C ~ {forecast['temp_max']}°C")